home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Screen savers / Basic Black 1.3.1 Folder / Basic Black / Source / BBinit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-30  |  24.9 KB  |  841 lines  |  [TEXT/R*ch]

  1. /*********************************************************************
  2.  * Basic Black ©1994 by Mason L. Bliss
  3.  * - version 1.3.1 - 1/30/1994 -
  4.  *
  5.  * The trap patching code is taken from a neat little program
  6.  * Mike Scanlin wrote for the August '92 MacTutor.
  7.  *
  8.  * Basic Black's screen-blanking routine was inspired by code written
  9.  * by Christopher Tate.
  10.  *
  11.  * Basic Black makes use of CShowProc, by Ken McLeod, to show its
  12.  * icon animation at startup time.
  13.  *
  14.  * Basic Black would be nowhere near as nice as it is without the
  15.  * consistent help of my beta testers, who are listed in the Basic Black
  16.  * read-me file. Thank you! ;-)
  17.  *
  18.  *********************************************************************/
  19.  
  20. /*********************************************************************
  21.  * Version History: (INIT code)
  22.  *
  23.  * 1.3.1    1/94
  24.  * Fixed the 'SAVC' selector so that it now follows the After Dark
  25.  * model. I had made an incorrect assumption about how it was supposed
  26.  * to work.
  27.  *
  28.  * 1.3    1/94
  29.  * Added support for After Dark's 'SAVR' gestalt selector, and added a
  30.  * 'BBlk' gestalt selector to tell the cdev where our patch globals are.
  31.  * Added support for AD's 'SAVC' screen saver control selector. Fixed
  32.  * the problem where some windows' boundaries would be upset on occasion.
  33.  * We now patch InitCursor and a couple other routines so that they don't
  34.  * do anything while we're asleep.
  35.  *
  36.  * 1.2    6/93
  37.  * Added an extra second to the lastAction patch global so as to avoid
  38.  * problems with having the screen wake up and go back to sleep
  39.  * immediately. Added optional bouncing clock. Added the ability to turn
  40.  * Basic Black off via the cdev. Cleaned code.
  41.  *
  42.  * 1.1    4/93
  43.  * Added user-configurability via ResEdit. Fixed support for multiple
  44.  * monitors, and fixed problem with moving Desktop icons while in the
  45.  * Finder.
  46.  *
  47.  * 1.0    2/93
  48.  * First public release. Included an unfortunate number of bugs. :/
  49.  *
  50.  *********************************************************************/
  51.  
  52.  
  53.  
  54. #include <Traps.h>
  55. #include <GestaltEqu.h>
  56. #include <ShutDown.h>
  57. #include "BB.h"
  58. #include "BBinit.h"
  59.  
  60.  
  61.  
  62. /*********************************************************************
  63.  * main:
  64.  *
  65.  * Gets memory in the system heap, installs our patches, and initializes
  66.  * our patch globals. This is the only routine that gets executed at
  67.  * startup time (by the INIT mechanism).
  68.  *
  69.  * Note that the code resource should be purgeable. The routines listed
  70.  * below aren't used - the code, after it's loaded, is COPIED into the
  71.  * area we set up in the system heap, after which the originals are
  72.  * discarded, as it's the COPIES that are used. If the code resource is
  73.  * set to "Not Purgeable" then the originals - which are never again
  74.  * referenced - stick around until shutdown time, wasting memory.
  75.  *
  76.  * Also, with Think C, make sure that "Custom Headers" is turned on.
  77.  *
  78.  * The block of memory that we allocate will look like this when main()
  79.  * has finished:
  80.  *
  81.  *                     +--------------------+
  82.  *                     |      PatchGlobals      |
  83.  *                     +--------------------+
  84.  *                     |  StartPatchCode()  |
  85.  *    WNE trap addr -> +--------------------+
  86.  *                     |  MyWaitNextEvent() |
  87.  *    GNE trap addr -> +--------------------+
  88.  *                     |  MyGetNextEvent()  |
  89.  *     IC trap addr -> +--------------------+
  90.  *                     |  MyInitCursor()    |
  91.  *    DMB trap addr -> +--------------------+
  92.  *                     |  MyDrawMenuBar()   |
  93.  *     ER trap addr -> +--------------------+
  94.  *                     |  MyEraseRect()     |
  95.  *     EO trap addr -> +--------------------+
  96.  *                     |  MyEraseOval()     |
  97.  *     EG trap addr -> +--------------------+
  98.  *                     |  MyEraseRgn()      |
  99.  *     ST trap addr -> +--------------------+
  100.  *                     |  MySystemTask()    |
  101.  *                     +--------------------+
  102.  *                     |  BBlkSelector()    |
  103.  *                     +--------------------+
  104.  *                     |  SAVRSelector()    |
  105.  *                     +--------------------+
  106.  *                     |  SAVCSelector()    |
  107.  *                     +--------------------+
  108.  *                     |  SaverControl()    |
  109.  *                     +--------------------+
  110.  *                     |  FallAsleep()      |
  111.  *                     +--------------------+
  112.  *                     |  WakeUp()          |
  113.  *                     +--------------------+
  114.  *                     |  abs()             |
  115.  *                     +--------------------+
  116.  *                     |  DrawClock()       |
  117.  *                     +--------------------+
  118.  *                     |  RemoveICPatch()   |
  119.  *                     +--------------------+
  120.  *                     |  EndPatchCode()    |
  121.  *                     +--------------------+
  122.  *
  123.  *********************************************************************/
  124. void main()
  125. {
  126.     Ptr                patchPtr;
  127.     PatchGlobalsPtr    pgPtr;
  128.     long            codeSize, offset, oldA5;
  129.     short            icon;
  130.     QDGlobals        qd;
  131.     GrafPort        gp;
  132.     Handle            HConfig, procH;
  133.     OSErr            theError;
  134.     Boolean            drawStartupIcon = false;
  135.     
  136.     /* try and get some memory in the system heap for code and globals */
  137.     codeSize = (long) EndPatchCode - (long) StartPatchCode;
  138.     patchPtr = NewPtrSys(codeSize + sizeof(PatchGlobals));
  139.     if (!patchPtr) {
  140.         /* put up the error icon */
  141.         if ((procH = GetIndResource('PROC', 1)) != 0L)    {
  142.                 HLock(procH);
  143.                 CallPascal(128, -1, *procH);
  144.                 HUnlock(procH);
  145.             }
  146.         return;    /* out of memory -- abort patching */
  147.     }
  148.     
  149.     /* initialize the patch globals at the beginning of the block */
  150.     pgPtr = (PatchGlobalsPtr) patchPtr;
  151.     pgPtr->pgOldSTA = (STAProcPtr) GetTrapAddress(_SetTrapAddress);
  152.     pgPtr->pgOldGTA = (GTAProcPtr) GetTrapAddress(_GetTrapAddress);
  153.     pgPtr->pgOldWNE = (WNEProcPtr) GetTrapAddress(_WaitNextEvent);
  154.     pgPtr->pgOldGNE = (GNEProcPtr) GetTrapAddress(_GetNextEvent);
  155.     pgPtr->pgOldIC = (ICProcPtr) GetTrapAddress(_InitCursor);
  156.     pgPtr->pgOldDMB = (DMBProcPtr) GetTrapAddress(_DrawMenuBar);
  157.     pgPtr->pgOldER = (ERProcPtr) GetTrapAddress(_EraseRect);
  158.     pgPtr->pgOldEO = (EOProcPtr) GetTrapAddress(_EraseOval);
  159.     pgPtr->pgOldEG = (EGProcPtr) GetTrapAddress(_EraseRgn);
  160.     pgPtr->pgOldST = (STProcPtr) GetTrapAddress(_SystemTask);
  161.  
  162.     /* move the code into place after the globals */
  163.     BlockMove(StartPatchCode, patchPtr + sizeof(PatchGlobals), codeSize);
  164.     
  165.     /* set the patches */
  166.     patchPtr += sizeof(PatchGlobals);
  167.     offset = (long) MyWaitNextEvent - (long) StartPatchCode;
  168.     SetTrapAddress((long) patchPtr + offset, _WaitNextEvent);
  169.     offset = (long) MyGetNextEvent - (long) StartPatchCode;
  170.     SetTrapAddress((long) patchPtr + offset, _GetNextEvent);
  171.     offset = (long) MyInitCursor - (long) StartPatchCode;
  172.     SetTrapAddress((long) patchPtr + offset, _InitCursor);
  173.     offset = (long) MyDrawMenuBar - (long) StartPatchCode;
  174.     SetTrapAddress((long) patchPtr + offset, _DrawMenuBar);
  175.     offset = (long) MyEraseRect - (long) StartPatchCode;
  176.     SetTrapAddress((long) patchPtr + offset, _EraseRect);
  177.     offset = (long) MyEraseOval - (long) StartPatchCode;
  178.     SetTrapAddress((long) patchPtr + offset, _EraseOval);
  179.     offset = (long) MyEraseRgn - (long) StartPatchCode;
  180.     SetTrapAddress((long) patchPtr + offset, _EraseRgn);
  181.     offset = (long) MySystemTask - (long) StartPatchCode;
  182.     SetTrapAddress((long) patchPtr + offset, _SystemTask);
  183.  
  184.     /* install a new 'BBlk' gestalt selector */
  185.     offset = (long) BBlkSelector - (long) StartPatchCode;
  186.     NewGestalt('BBlk', (ProcPtr) ((long) patchPtr + offset));
  187.  
  188.     /* install a new 'SAVR' gestalt selector */
  189.     offset = (long) SAVRSelector - (long) StartPatchCode;
  190.     NewGestalt('SAVR', (ProcPtr) ((long) patchPtr + offset));
  191.  
  192.     /* install a new 'SAVC' gestalt selector */
  193.     offset = (long) SAVCSelector - (long) StartPatchCode;
  194.     NewGestalt('SAVC', (ProcPtr) ((long) patchPtr + offset));
  195.     
  196.     /* install shutdown procedure */
  197.     offset = (long) RemoveICPatch - (long) StartPatchCode;
  198.     ShutDwnInstall( (ShutDwnProcPtr) ((long) patchPtr + offset),
  199.                                         sdRestartOrPower + sdOnDrivers);
  200.  
  201.     pgPtr->pgSaverOn = false;
  202.     pgPtr->pgPatchesIn = false;
  203.     pgPtr->pgMustSleep = false;
  204.     pgPtr->pgInSleepRect = false;
  205.     pgPtr->pgLastAction = Ticks;
  206.     pgPtr->pgLastRefresh = Ticks;
  207.     
  208.     
  209.     /* Read the configuration information from our 'PREF' resource. */
  210.     HConfig = GetIndResource('PREF', 1);        // get the first 'PREF'
  211.     pgPtr->pgSleepRect = (short) **HConfig;
  212.     pgPtr->pgWakeRect = (short) *(*HConfig + 1);
  213.     pgPtr->pgIdleTicks = (long) ((short) *(*HConfig + 2)) * 3600;
  214.     pgPtr->pgBouncingClock = (Boolean) *(*HConfig + 3);
  215.     pgPtr->pgMustSave = (Boolean) *(*HConfig + 4);
  216.     pgPtr->pgRefreshTime = ((short) *(*HConfig + 5)) * 60;
  217.     if (((short) *(*HConfig + 6)) == 1)
  218.         drawStartupIcon = true;
  219.     if (((short) *(*HConfig + 7)) == 0) {        // Fade to black
  220.         StuffHex(&(pgPtr->pgForePat), "\p0000000000000000");
  221.         StuffHex(&(pgPtr->pgBackPat), "\pFFFFFFFFFFFFFFFF");
  222.     } else {                                    // Fade to white
  223.         StuffHex(&(pgPtr->pgForePat), "\pFFFFFFFFFFFFFFFF");
  224.         StuffHex(&(pgPtr->pgBackPat), "\p0000000000000000");
  225.     }
  226.     pgPtr->pgMenubarKluge = (Boolean) *(*HConfig + 8);
  227.     ReleaseResource(HConfig);
  228.     
  229.     
  230.     /* Read in the clock background picture. */
  231.     pgPtr->pgClockBg = (PicHandle) GetResource('PICT', 255);
  232.     DetachResource(pgPtr->pgClockBg);
  233.  
  234.     
  235.     /* The next three lines fake out some QuickDraw globals so we can
  236.         figure out the correct sleep and wake rectangles, as we don't
  237.         have access to the 'proper' QuickDraw globals. */
  238.     oldA5 = SetA5((long) &qd.qdend);    // Point A5 at our fake globals
  239.     InitGraf(&qd.thePort);                // ...and initialize 'em
  240.     OpenPort((GrafPtr) &gp);
  241.         
  242.     pgPtr->pgCorners[0].left = (gp.portRect).left - 1;        // top left
  243.     pgPtr->pgCorners[0].right = (gp.portRect).left + 8;
  244.     pgPtr->pgCorners[0].top = (gp.portRect).top - 1;
  245.     pgPtr->pgCorners[0].bottom = (gp.portRect).top + 8;
  246.  
  247.     pgPtr->pgCorners[1].left = (gp.portRect).right - 8;        // top right
  248.     pgPtr->pgCorners[1].right = (gp.portRect).right + 1;
  249.     pgPtr->pgCorners[1].top = (gp.portRect).top - 1;
  250.     pgPtr->pgCorners[1].bottom = (gp.portRect).top + 8;
  251.  
  252.     pgPtr->pgCorners[2].left = (gp.portRect).right - 8;        // bottom right
  253.     pgPtr->pgCorners[2].right = (gp.portRect).right + 1;
  254.     pgPtr->pgCorners[2].top = (gp.portRect).bottom - 8;
  255.     pgPtr->pgCorners[2].bottom = (gp.portRect).bottom + 1;
  256.  
  257.     pgPtr->pgCorners[3].left = (gp.portRect).left - 1;        // bottom left
  258.     pgPtr->pgCorners[3].right = (gp.portRect).left + 8;
  259.     pgPtr->pgCorners[3].top = (gp.portRect).bottom - 8;
  260.     pgPtr->pgCorners[3].bottom = (gp.portRect).bottom + 1;
  261.     
  262.     ClosePort((GrafPtr) &gp);
  263.     oldA5 = SetA5(oldA5);                // Restore A5 to its previous value
  264.  
  265.  
  266.     /* put up our icon animation because the nice user wants us to */
  267.     if (drawStartupIcon && (procH = GetIndResource('PROC', 1)) != 0L)    {
  268.         HLock(procH);
  269.         /* display our cool little animation */
  270.         for (icon = 128; icon < 133; ++icon) {
  271.             offset = TickCount() + 5;
  272.             CallPascal(icon, 0, *procH);
  273.             while (offset > TickCount());
  274.         }
  275.         /* put up either the final icon, or the 'off' icon, as appropriate */
  276.         if (pgPtr->pgMustSave)
  277.             CallPascal(-4064, -1, *procH);
  278.         else
  279.             CallPascal(257, -1, *procH);
  280.         HUnlock(procH);
  281.     }
  282. }
  283.  
  284.  
  285.  
  286. /*********************************************************************
  287.  * StartPatchCode:
  288.  *
  289.  * Dummy proc to mark the beginning of the code for the
  290.  * patches. Make sure all of your patch code is between
  291.  * here and EndPatchCode.
  292.  *
  293.  *********************************************************************/
  294. void StartPatchCode()
  295. {
  296. }
  297.  
  298.  
  299.  
  300. /*********************************************************************
  301.  * MyWaitNextEvent:
  302.  *
  303.  * Tail patch on WaitNextEvent.
  304.  *
  305.  * The reason this returns a short instead of a Boolean is because we
  306.  * need to make sure the low byte of the top word on the stack is zero
  307.  * because some programs do a Tst.W (SP)+ when this returns instead of
  308.  * Tst.B (SP)+ like they should (which is technically their bug but, we
  309.  * might as well work around it since it's not hard).
  310.  *
  311.  * If you want to eat the event (not pass it on to the caller) then set
  312.  * returnValue to zero.
  313.  *
  314.  *********************************************************************/
  315. pascal short MyWaitNextEvent(short eventMask, EventRecord *theEvent, long sleep, RgnHandle mouseRgn)
  316. {
  317.     PatchGlobalsPtr    pgPtr;
  318.     short            returnValue;
  319.     register short    foo;
  320.     
  321.     /* find our globals */
  322.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  323.     
  324.     /* call original GNE first */
  325.     returnValue = (*pgPtr->pgOldWNE) (eventMask, theEvent, sleep, mouseRgn);
  326.     
  327.     if ((foo = theEvent->what) >= 1 && foo <= 5 || foo == 7) {
  328.         pgPtr->pgLastAction = Ticks;        /* Update last time counter */
  329.         pgPtr->pgInSleepRect = false;
  330.         if (pgPtr->pgSaverOn) {
  331.             foo = theEvent->what;
  332.             if (foo >= 3 && foo <= 5) {        /* mask keypresses and clicks */
  333.                 returnValue = 0;
  334.                 theEvent->what = nullEvent;
  335.             }
  336.             WakeUp();
  337.         }
  338.     }
  339.     
  340.     /* return to original caller */
  341.     return returnValue;
  342. }
  343.  
  344.  
  345.  
  346. /*********************************************************************
  347.  * MyGetNextEvent:
  348.  *
  349.  * Tail patch on GetNextEvent.
  350.  *
  351.  * The reason this returns a short instead of a Boolean is because we
  352.  * need to make sure the low byte of the top word on the stack is zero
  353.  * because some programs do a Tst.W (SP)+ when this returns instead of
  354.  * Tst.B (SP)+ like they should (which is technically their bug but, we
  355.  * might as well work around it since it's not hard).
  356.  *
  357.  * If you want to eat the event (not pass it on to the caller) then set
  358.  * returnValue to zero.
  359.  *
  360.  *********************************************************************/
  361. pascal short MyGetNextEvent(short eventMask, EventRecord *theEvent)
  362. {
  363.     PatchGlobalsPtr    pgPtr;
  364.     short            returnValue;
  365.     register short    foo;
  366.     
  367.     /* find our globals */
  368.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  369.     
  370.     /* call original GNE first */
  371.     returnValue = (*pgPtr->pgOldGNE) (eventMask, theEvent);
  372.     
  373.     if ((foo = theEvent->what) >= 1 && foo <= 5 || foo == 7) {
  374.         pgPtr->pgLastAction = Ticks;        /* Update last time counter */
  375.         pgPtr->pgInSleepRect = false;
  376.         if (pgPtr->pgSaverOn) {
  377.             foo = theEvent->what;
  378.             if (foo >= 3 && foo <= 5) {        /* mask keypresses and clicks */
  379.                 returnValue = 0;
  380.                 theEvent->what = nullEvent;
  381.             }
  382.             WakeUp();
  383.         }
  384.     }
  385.     
  386.     /* return to original caller */
  387.     return returnValue;
  388. }
  389.  
  390.  
  391.  
  392. /*********************************************************************
  393.  * MyInitCursor:
  394.  *
  395.  * Patch that disables InitCursor if we're asleep.
  396.  *
  397.  *********************************************************************/
  398. pascal void MyInitCursor()
  399. {
  400.     PatchGlobalsPtr    pgPtr;
  401.     
  402.     /* find our globals */
  403.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  404.     
  405.     /* call original IC first */
  406.     if (pgPtr->pgSaverOn == false)
  407.         (*pgPtr->pgOldIC) ();
  408. }
  409.  
  410.  
  411.  
  412. /*********************************************************************
  413.  * MyDrawMenuBar:
  414.  *
  415.  * Patch that disables DrawMenuBar if we're asleep.
  416.  *
  417.  *********************************************************************/
  418. pascal void MyDrawMenuBar()
  419. {
  420.     PatchGlobalsPtr    pgPtr;
  421.     
  422.     /* find our globals */
  423.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  424.     
  425.     /* call original DMB first */
  426.     if (pgPtr->pgSaverOn == false)
  427.         (*pgPtr->pgOldDMB) ();
  428. }
  429.  
  430.  
  431.  
  432. /*********************************************************************
  433.  * MyEraseRect:
  434.  *
  435.  * Patch that disables EraseRect if we're asleep.
  436.  *
  437.  *********************************************************************/
  438. pascal void MyEraseRect(Rect *rect)
  439. {
  440.     PatchGlobalsPtr    pgPtr;
  441.     
  442.     /* find our globals */
  443.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  444.     
  445.     /* call original ER first */
  446.     if (pgPtr->pgSaverOn == false)
  447.         (*pgPtr->pgOldER) (rect);
  448. }
  449.  
  450.  
  451.  
  452. /*********************************************************************
  453.  * MyEraseOval:
  454.  *
  455.  * Patch that disables EraseOval if we're asleep.
  456.  *
  457.  *********************************************************************/
  458. pascal void MyEraseOval(Rect *rect)
  459. {
  460.     PatchGlobalsPtr    pgPtr;
  461.     
  462.     /* find our globals */
  463.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  464.     
  465.     /* call original EO first */
  466.     if (pgPtr->pgSaverOn == false)
  467.         (*pgPtr->pgOldEO) (rect);
  468. }
  469.  
  470.  
  471.  
  472. /*********************************************************************
  473.  * MyEraseRgn:
  474.  *
  475.  * Patch that disables EraseRgn if we're asleep.
  476.  *
  477.  *********************************************************************/
  478. pascal void MyEraseRgn(RgnHandle rgn)
  479. {
  480.     PatchGlobalsPtr    pgPtr;
  481.     
  482.     /* find our globals */
  483.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  484.     
  485.     /* call original EG first */
  486.     if (pgPtr->pgSaverOn == false)
  487.         (*pgPtr->pgOldEG) (rgn);
  488. }
  489.  
  490.  
  491.  
  492. /*********************************************************************
  493.  * MySystemTask:
  494.  *
  495.  * Patches SystemTask. This is where most of our maintenance is done.
  496.  *
  497.  *********************************************************************/
  498. pascal void MySystemTask(void)
  499. {
  500.     PatchGlobalsPtr    pgPtr;
  501.     Point            mousePt;
  502.     
  503.     /* find our globals */
  504.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  505.  
  506.     /* Check to see if the mouse has moved */
  507.     GetMouse(&mousePt);
  508.     LocalToGlobal(&mousePt);
  509.     if (!EqualPt(mousePt, pgPtr->pgLastMouse)) {    /* id est, mouse moved */
  510.         pgPtr->pgLastMouse = mousePt;
  511.         if (pgPtr->pgSaverOn)
  512.             WakeUp();
  513.         else {
  514.             pgPtr->pgLastAction = Ticks;
  515.             pgPtr->pgInSleepRect = false;
  516.         }
  517.     }
  518.  
  519.     /* Does the nice user want us to fall asleep now? */
  520.     if (!(pgPtr->pgInSleepRect) && PtInRect(mousePt, &pgPtr->pgCorners[pgPtr->pgSleepRect])) {
  521.         pgPtr->pgInSleepRect = true;
  522.         /* Set the clocks back so it looks like we haven't moved for a while. */
  523.         pgPtr->pgLastAction -= ((pgPtr->pgIdleTicks) - DELAYTICKS);
  524.     }
  525.     
  526.     /* Is the mouse in the Never Sleep corner? */
  527.     if (PtInRect(mousePt, &pgPtr->pgCorners[pgPtr->pgWakeRect]))
  528.         pgPtr->pgLastAction = Ticks;
  529.     
  530.     /* Has enough idle time passed for us to fall asleep naturally? */
  531.     if (((Ticks - (pgPtr->pgLastAction)) > (pgPtr->pgIdleTicks))
  532.             && !(pgPtr->pgSaverOn))
  533.         pgPtr->pgMustSleep = true;
  534.     
  535.     /* Do sleepy-time maintenance. */
  536.     if (pgPtr->pgSaverOn) {
  537.         /* Take care of the refresh timing */
  538.         if ((Ticks - pgPtr->pgLastRefresh) > pgPtr->pgRefreshTime) {
  539.             pgPtr->pgLastRefresh = Ticks;
  540.             pgPtr->pgMustSleep = true;
  541.         }
  542.         
  543.         /* Has the menu been messed with? (Don't mess with me, man!) */
  544.         /* Canvas users truly hate this. It's Aldus' fault - not mine. */
  545.         if (pgPtr->pgMenubarKluge && MBarHeight != 0)
  546.             pgPtr->pgMustSleep = true;
  547.     }
  548.     
  549.     /* Do we need to fall asleep? */
  550.     if (pgPtr->pgMustSleep && pgPtr->pgMustSave)
  551.         FallAsleep();
  552.     
  553.     /* Now give everyone else some time! */
  554.     (*pgPtr->pgOldST) ();
  555. }
  556.  
  557.  
  558.  
  559. /*********************************************************************
  560.  * BBlkSelector:
  561.  *
  562.  * 'BBlk' gestalt selector that provides the address of our patch
  563.  * globals. This is *much* cleaner than patching SizeResource().
  564.  *
  565.  *********************************************************************/
  566. pascal void BBlkSelector(OSType selector, long *result)
  567. {
  568.     PatchGlobalsPtr    pgPtr;
  569.     
  570.     /* find our globals */
  571.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  572.     
  573.     /* ...and give 'em back! */
  574.     *result = (long) pgPtr;
  575. }
  576.  
  577.  
  578.  
  579. /*********************************************************************
  580.  * SAVRSelector:
  581.  *
  582.  * 'SAVR' gestalt selector that tells the world when we're alive and
  583.  * also when we're sleeping.
  584.  *
  585.  *********************************************************************/
  586. pascal void SAVRSelector(OSType selector, long *result)
  587. {
  588.     PatchGlobalsPtr    pgPtr;
  589.     
  590.     /* find our globals */
  591.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  592.  
  593.     *result = 0;
  594.  
  595.     /* if we haven't been disabled, tell the world of our presence */
  596.     if (pgPtr->pgMustSave == true)
  597.         *result |= 1;                // saver enabled
  598.  
  599.     /* If screen is black, tell the world we're alive */
  600.     if (pgPtr->pgSaverOn == true) {
  601.         *result |= 2;                // saver asleep
  602.         *result |= 16;                // app drawing disabled
  603.     }
  604. }
  605.  
  606.  
  607.  
  608. /*********************************************************************
  609.  * SAVCSelector:
  610.  *
  611.  * The 'SAVC' gestalt selector passes back the address of our control
  612.  * routine so that other applications can control us.
  613.  *
  614.  * We can be turned on and off remotely, and we can be told to put
  615.  * the screen to sleep and wake the screen up. Neat, huh?
  616.  *
  617.  *********************************************************************/
  618. pascal void SAVCSelector(OSType selector, long *result)
  619. {
  620.     *result = (long) SaverControl;
  621. }
  622.  
  623.  
  624.  
  625. /*********************************************************************
  626.  * SaverControl:
  627.  *
  628.  * Executes a command from an application.
  629.  *
  630.  *********************************************************************/
  631. pascal OSErr SaverControl(short theCommand)
  632. {
  633.     PatchGlobalsPtr    pgPtr;
  634.     
  635.     /* find our globals */
  636.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  637.  
  638.     switch (theCommand) {
  639.         case eSaverWakeUp:                    // Make Basic Black wake up.
  640.             if (pgPtr->pgSaverOn)
  641.                 WakeUp();
  642.             break;
  643.             
  644.         case eSaverSleep:                    // Make Basic Black go to sleep.
  645.             pgPtr->pgMustSleep = true;
  646.             break;
  647.             
  648.         case eSaverOn:                        // Turn Basic Black on.
  649.             pgPtr->pgMustSave = true;
  650.             pgPtr->pgMustSleep = false;
  651.             break;
  652.             
  653.         case eSaverOff:                        // Turn Basic Black off.
  654.             pgPtr->pgMustSave = false;
  655.             break;
  656.     }
  657.     
  658.     return noErr;
  659. }
  660.  
  661.  
  662.  
  663. /*********************************************************************
  664.  * FallAsleep:
  665.  *
  666.  * This is where we black out the screen.
  667.  *
  668.  *********************************************************************/
  669. void FallAsleep(void)
  670. {
  671.     PatchGlobalsPtr    pgPtr;
  672.     GrafPort        myPort;
  673.     GrafPtr            oldPort;
  674.     
  675.     /* find our globals */
  676.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  677.     
  678.     pgPtr->pgSaverOn = true;
  679.     pgPtr->pgMustSleep = false;
  680.     pgPtr->pgLastRefresh = Ticks;
  681.  
  682.     /* hide the cursor */
  683.     ObscureCursor();
  684.     
  685.     /* Fix the menu bar if the kluge is on and the MBarHeight's been changed */
  686.     if (pgPtr->pgMenubarKluge && MBarHeight != 0) {
  687.         pgPtr->pgOldHeight = MBarHeight;
  688.         MBarHeight = 0;
  689.     }
  690.     
  691.     /* Save the old port and set our new characteristics */
  692.     GetPort(&oldPort);
  693.     OpenPort(&myPort);
  694.     PenPat(pgPtr->pgForePat);
  695.     BackPat(pgPtr->pgBackPat);
  696.     
  697.     /* Get a region for everything, and paint it. */
  698.     UnionRgn(GrayRgn, myPort.visRgn, myPort.visRgn);
  699.     FillRgn(myPort.visRgn, pgPtr->pgBackPat);
  700.     
  701.     /* Draw our clock, if it's turned on. */
  702.     if (pgPtr->pgBouncingClock)
  703.         DrawClock(&myPort);
  704.     
  705.     /* Empty the visRgns of all windows */
  706.     CopyRgn(GrayRgn, myPort.visRgn);
  707.     SetEmptyRgn(GrayRgn);
  708.     CalcVisBehind((WindowPeek) FrontWindow(), myPort.visRgn);
  709.     CopyRgn(myPort.visRgn, GrayRgn);
  710.  
  711.     /* Restore the old port */
  712.     ClosePort(&myPort);
  713.     SetPort(oldPort);
  714. }
  715.  
  716.  
  717.  
  718. /*********************************************************************
  719.  * WakeUp:
  720.  *
  721.  * This is where we liven up.
  722.  *
  723.  *********************************************************************/
  724. void WakeUp(void)
  725. {
  726.     PatchGlobalsPtr    pgPtr;
  727.     GrafPtr            oldPort;
  728.     
  729.     /* find our globals */
  730.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  731.     
  732.     pgPtr->pgSaverOn = false;
  733.     pgPtr->pgInSleepRect = false;
  734.     pgPtr->pgLastAction = Ticks + 60;        /* can't sleep for a second */
  735.     
  736.     /* restore the menu bar */
  737.     if (pgPtr->pgMenubarKluge)
  738.         MBarHeight = pgPtr->pgOldHeight;
  739.     DrawMenuBar();
  740.     
  741.     /* Grab the current port. */
  742.     GetPort(&oldPort);
  743.     
  744.     /* Redraw window frames and set update regions equal to visible regions */
  745.     PaintBehind((WindowPeek) FrontWindow(), GrayRgn);
  746.  
  747.     /* Calculate visRgns */
  748.     CalcVisBehind((WindowPeek) FrontWindow(), GrayRgn);
  749.     
  750.     /* Reset the port - the previous two toolbox calls disrupt it. */
  751.     SetPort(oldPort);
  752. }
  753.  
  754.  
  755.  
  756. /*********************************************************************
  757.  * abs:
  758.  *
  759.  * Return the absolute value of a short.
  760.  *
  761.  *********************************************************************/
  762. short abs(short x)
  763. {
  764.     return x < 0 ? -x : x;
  765. }
  766.  
  767.  
  768.  
  769. /*********************************************************************
  770.  * DrawClock:
  771.  *
  772.  * Draw an analog clock at a random place in the given
  773.  * GrafPort.
  774.  *
  775.  *********************************************************************/
  776. void DrawClock(GrafPtr myPort)
  777. {
  778.     PatchGlobalsPtr    pgPtr;
  779.     DateTimeRec        theTime;
  780.     Rect            theTimeRect;
  781.     short            mid;
  782.     
  783.     /* find our globals */
  784.     pgPtr = (PatchGlobalsPtr) ((long) StartPatchCode - sizeof(PatchGlobals));
  785.  
  786.     GetTime(&theTime);
  787.     
  788.     /* find our clock rectangle */
  789.     theTimeRect.top = (abs(Random()) % (myPort->portRect.bottom - 110)) + 5;
  790.     theTimeRect.bottom = theTimeRect.top + 100;
  791.     theTimeRect.left = (abs(Random()) % (myPort->portRect.right - 110)) + 5;
  792.     theTimeRect.right = theTimeRect.left + 100;
  793.     
  794.     /* draw our background */
  795.     DrawPicture(pgPtr->pgClockBg, &theTimeRect);
  796.     
  797.     /* move the rect in, and draw the minute hand */
  798.     InsetRect(&theTimeRect, 5, 5);
  799.     PaintArc(&theTimeRect, (theTime.minute) * 6 - 3, 6);
  800.     
  801.     /* move the rect in more, and draw the hour hand */
  802.     InsetRect(&theTimeRect, 15, 15);
  803.     PaintArc(&theTimeRect, (theTime.hour % 12) * 30 - 5 + (theTime.minute / 2), 10);    
  804. }
  805.  
  806.  
  807.  
  808. /*********************************************************************
  809.  * RemoveICPatch:
  810.  *
  811.  * Shutdown procedure that replaces the InitCursor patch.
  812.  *
  813.  * This is an unspeakably evil way to do this, but since our patch
  814.  * globals are destroyed before shutdown, leading to our InitCursor
  815.  * routine gagging and dying horribly when called, there isn't any
  816.  * better way to do this that I know of. Blame Apple for not preserving
  817.  * memory to the end.
  818.  *
  819.  * Anyways, yes, this *is* what it looks like. For shutdown purposes,
  820.  * we're replacing InitCursor with ShowCursor... :/
  821.  *
  822.  *********************************************************************/
  823. pascal void RemoveICPatch()
  824. {
  825.     SetTrapAddress(GetTrapAddress(_ShowCursor), _InitCursor);
  826. }
  827.  
  828.  
  829.  
  830. /*********************************************************************
  831.  * EndPatchCode:
  832.  *
  833.  * Dummy proc to mark the end of the code for the patches.
  834.  * Make sure all of your patch code is between here and
  835.  * StartPatchCode.
  836.  *
  837.  *********************************************************************/
  838. void EndPatchCode()
  839. {
  840. }
  841.